package com.cms.hotfix.utils;

import android.app.Application;
import android.content.Context;
import android.media.session.PlaybackState;
import android.os.Build;
import android.util.Log;

import com.android.baselibrary.app.MainApplication;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * AUTHOR：YF
 * DATE: 2024/1/8 0008
 * DESCRIBE: 修复包已经放在asset下
 *
 * @author GW00286792
 */
public class HotFixJavaUtils {

    public static void inject(MainApplication application, String filePath) {
        if (!new File(filePath).exists()) {
            return;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return;
        }
        // sdk23的修改
        //1. 获取程序的PathClassLoader
        ClassLoader classLoader = application.getClassLoader();
        try {
            //2. 反射获取PathClassLoader的父类的BaseDexClassLoader的pathList对象。
            Field pathList = ReflexJavaUtils.findField(classLoader, "pathList");
            //3. 反射获得pathList的dexElements对象,静态属性获取对象时不需要传任何参数
            Object dexPathList = pathList.get(classLoader);

            //4. 把补丁包变成Element数组:patchElement（反射执行makePathElements）
            ArrayList<IOException> suppressedExceptions = new ArrayList<>();
            ArrayList<File> fields = new ArrayList<>();
            fields.add(new File(filePath));

            Object[] newDexElementsObj = makePathElements(dexPathList, new ArrayList<>(fields), application.getCacheDir(), suppressedExceptions);
            Field dexElementsField = ReflexJavaUtils.findField(dexPathList, "dexElements");
            Object[] oldDexElementsObj = (Object[]) dexElementsField.get(dexPathList);

            //5.数据合并
            Object[] resultElement = (Object[]) Array.newInstance(oldDexElementsObj.getClass().getComponentType(), oldDexElementsObj.length + newDexElementsObj.length);
            System.arraycopy(newDexElementsObj, 0, resultElement, 0, newDexElementsObj.length);
            System.arraycopy(oldDexElementsObj, 0, resultElement, newDexElementsObj.length, oldDexElementsObj.length);
            dexElementsField.set(dexPathList, resultElement);

        } catch (Exception e) {
        }
    }

    /**
     * 把dex转化为Element数组
     */
    private static Object[] makePathElements(Object dexPathList, ArrayList<File> files, File optimizedDirectory, ArrayList<IOException> suppressedExceptions) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        //通过阅读android6、7、8、9源码，都存在makePathElements方法
        Method makePathElements = ReflexJavaUtils.findMethod(dexPathList, "makePathElements", List.class, File.class, List.class);
        return (Object[]) makePathElements.invoke(dexPathList, files, optimizedDirectory, suppressedExceptions);
    }

    private static File initHack(Context context) {

        File hackDir = context.getDir("hack", Context.MODE_PRIVATE);

        File hackFile = new File(hackDir, "hotfix.jar");
        if (!hackFile.exists()) {
            BufferedInputStream is = null;
            BufferedOutputStream os = null;
            try {
                is = new BufferedInputStream(context.getAssets().open("hotfix" + ".jar"));
                os = new BufferedOutputStream(new FileOutputStream(hackFile));
                byte[] buffer = new byte[4096];
                int len;
                while ((len = is.read(buffer)) != -1) {
                    os.write(buffer, 0, len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return hackFile;

    }
}
